#include "WalkerGroupOpenCL.h"

#define CHECKERR(cnd, errString, label)\
  if(cnd)\
{\
  snprintf(errorString,\
           sizeof(errorString) - 1,\
           "Error %s:%d: %s\n",\
           __FILE__,\
           __LINE__,\
           errString);\
  goto label;\
}

bool WalkerGroupOpenCL::runtimeInitialized = false;
static char errorString[1024];

static size_t getGeneratorSize()
{
  unsigned int genSize;
  size_t localSize = 1;
  size_t globalSize = 1;
  cl_mem genSizeDevice;

  clUtil::alloc(sizeof(genSize), &genSizeDevice);

  clUtil_enqueueKernel("clSPRNG_getGeneratorSize",
                       1,
                       &globalSize,
                       &localSize,
                       genSizeDevice);
  clUtil::deviceGet(&genSize, sizeof(genSize), genSizeDevice);
  clUtil::free(genSizeDevice);
  
  return genSize;
}

static void generateParams()
{
  ofstream header("params.hcl");

  header.setf(ios_base::scientific);

  header << "__constant float alphaMcMillan = " << alphaMcMillan << "f;" << endl;
  header << "__constant float nu = " << nu << "f;" << endl;
  header << "__constant float b = " << b << "f;" << endl;

  header << "__constant float c00 = " << c00 << "f;" << endl;
  header << "__constant float c01 = " << c01 << "f;" << endl;
  header << "__constant float c02 = " << c02 << "f;" << endl;
  header << "__constant float c10 = " << c10 << "f;" << endl;
  header << "__constant float c11 = " << c11 << "f;" << endl;
  header << "__constant float c12 = " << c12 << "f;" << endl;
  header << "__constant float c20 = " << c20 << "f;" << endl;
  header << "__constant float c21 = " << c21 << "f;" << endl;
  header << "__constant float c22 = " << c22 << "f;" << endl;

  header << "__constant float p0 = " << p0 << "f;" << endl;
  header << "__constant float p1 = " << p0 << "f;" << endl;
  header << "__constant float p2 = " << p0 << "f;" << endl;
  header << "__constant float p3 = " << p0 << "f;" << endl;
  header << "__constant float p4 = " << p0 << "f;" << endl;

  header << "__constant float rm = " << rm << "f;" << endl;
  header << "__constant float A = " << A << "f;" << endl;
  header << "__constant float alphaPotential = " 
         << alphaPotential
         << "f;" 
         << endl;
  header << "__constant float beta = " << beta << "f;" << endl;
  header << "__constant float epsilon = " << epsilon << "f;" << endl;
  header << "__constant float c6_pot = " << c6_pot << "f;" << endl;
  header << "__constant float c8_pot = " << c8_pot << "f;" << endl;
  header << "__constant float c9_pot = " << c9_pot << "f;" << endl;
  header << "__constant float c10_pot = " << c10_pot << "f;" << endl;
  header << "__constant float D = " << D << "f;" << endl;
  header << "__constant double PARTICLE_MASS = " 
         << gParticleMass 
         << ";" 
         << endl;
  header << "__constant int seed = " << gRandomSeed << ";" << endl;
  header << "__constant float stepSize = " << gStepSize << "f;" << endl;

  header.close();
}

void WalkerGroupOpenCL::allocate(unsigned int numParticles, 
                                 unsigned int numWalkers)
{
  if(WalkerGroupOpenCL::runtimeInitialized == false)
  {
    const char* kernelNames[] = {"OpenCL/sprng/sprng.cl","OpenCL/kernel.cl"};
    char options[1024];
    char* mchamrPath;
    char const* waveFunctionName;
    size_t numParticles = gXDim * gYDim * gZDim;

    //Get the path to mchamr
    mchamrPath = getenv("MCHAMR_PATH");

    //chdir into mchamr directory
    chdir(mchamrPath);

    //create header file containing constants
    generateParams();

    //Get which wavefunction we're using
    switch(getWaveFunction())
    {
      case kWaveFunctionDefault:
        waveFunctionName = "DEFAULT_WAVE_FUNCTION";
        break;
      case kWaveFunctionMcMillan:
        waveFunctionName = "MCMILLAN_WAVE_FUNCTION";
        break;
      default:
        printf("Error GridOpenCLInit(): Invalid wave function.\n");
        break;
    }

    snprintf(options, 
             sizeof(options) - 1, "-D%s -I%s",
             waveFunctionName,
             mchamrPath);

    if(clUtil::initialize(kernelNames, 2, NULL, options) != CL_SUCCESS)
    {
      printf("Could not initialize OpenCL runtime. Aborting.");
      exit(1);
    }

    WalkerGroupOpenCL::runtimeInitialized = true;
  }

  //Allocate memory for calculations
  clUtil::alloc(numWalkers * numParticles * sizeof(double), 
                &mPartialPotential);
  clUtil::alloc(numWalkers * numParticles * sizeof(double), 
                &mPartialWaveFunction);
  clUtil::alloc(numWalkers * numParticles * sizeof(double), 
                &mPartialKinetic);
  clUtil::alloc(numWalkers * sizeof(unsigned int), &mCurBuffer);
  clUtil::alloc(2 * numWalkers * numParticles * sizeof(float), &mX);
  clUtil::alloc(2 * numWalkers * numParticles * sizeof(float), &mY);
  clUtil::alloc(2 * numWalkers * numParticles * sizeof(float), &mZ);
  clUtil::alloc(numParticles * sizeof(double), &mQForceX);
  clUtil::alloc(numParticles * sizeof(double), &mQForceY);
  clUtil::alloc(numParticles * sizeof(double), &mQForceZ);
  clUtil::alloc(numWalkers * sizeof(double), &mTotalEnergy);
  clUtil::alloc(2 * numWalkers * sizeof(double), &mKinetic);
  clUtil::alloc(2 * numWalkers * sizeof(double), &mPotential);
  clUtil::alloc(2 * numWalkers * sizeof(double), &mWaveFunction);
#ifdef GPU_SPRNG
  clUtil::alloc(numWalkers * getGeneratorSize(), &mGenerators);
#else
  mGenerators = new int*[numWalkers];
  mRandomBuffer = new float[3 * numParticles * numWalkers];
  clUtil::alloc(3 * numParticles * numWalkers * sizeof(float), 
                &mRandomBufferGPU);
#endif

  mNumIterations = 0;
}

WalkerGroupOpenCL::WalkerGroupOpenCL(WalkerGroup* initial,
                                     unsigned int numWalkers,
                                     unsigned int walkersStart)
{
  mNumWalkers = numWalkers;
  mNumParticles = initial->getNumParticles();
  mWalkerStart = walkersStart;

  float* x = new float[mNumParticles];
  float* y = new float[mNumParticles];
  float* z = new float[mNumParticles];
  float* xStaging = new float[mNumWalkers * mNumParticles];
  float* yStaging = new float[mNumWalkers * mNumParticles];
  float* zStaging = new float[mNumWalkers * mNumParticles];
  unsigned int* curBuffer = new unsigned int[mNumWalkers];

  this->allocate(mNumParticles, mNumWalkers);

  //Get particles of first walker of initial walker group
  initial->getX(x);
  initial->getY(y);
  initial->getZ(z);

  //Put the particles in a staging buffer for each walker of this group
  for(unsigned int curWalker = 0; curWalker < mNumWalkers; curWalker++)
  {
    for(unsigned int curParticle = 0;
        curParticle < mNumParticles; 
        curParticle++)
    {
      xStaging[curWalker * mNumParticles + curParticle] = x[curParticle];
      yStaging[curWalker * mNumParticles + curParticle] = y[curParticle];
      zStaging[curWalker * mNumParticles + curParticle] = z[curParticle];
    }

    curBuffer[curWalker] = 1;
  }

  clUtil::devicePut(xStaging, 
                    mNumParticles * mNumWalkers * sizeof(xStaging[0]), 
                    mX);
  clUtil::devicePut(yStaging,
                    mNumParticles * mNumWalkers * sizeof(yStaging[0]), 
                    mY);
  clUtil::devicePut(zStaging, 
                    mNumParticles * mNumWalkers * sizeof(zStaging[0]), 
                    mZ);
  clUtil::devicePut(curBuffer, mNumWalkers * sizeof(curBuffer[0]), mCurBuffer);

  this->initialize();

  clFinish(clUtil::getCommandQueue());
  
  delete[] x;
  delete[] y;
  delete[] z;
  delete[] xStaging;
  delete[] yStaging;
  delete[] zStaging;
  delete[] curBuffer;
}

WalkerGroupOpenCL::WalkerGroupOpenCL(unsigned int xDim,
                                     unsigned int yDim,
                                     unsigned int zDim)
{
  mNumParticles = xDim * yDim * zDim;
  mNumWalkers = 1;
  mWalkerStart = 0;

  float* x = new float[mNumParticles];
  float* y = new float[mNumParticles];
  float* z = new float[mNumParticles];
  unsigned int curBuffer = 1;
  unsigned int curParticle = 0;

  this->allocate(mNumParticles, mNumWalkers);

  for(unsigned int i = 0; i < xDim; i++)
  {
    for(unsigned int j = 0; j < yDim; j++)
    {
      for(unsigned int k = 0; k < zDim; k++)
      {
        x[curParticle] = i * GRID_SPACING;
        y[curParticle] = j * GRID_SPACING;
        z[curParticle] = k * GRID_SPACING;
        curParticle++;
      }
    }
  }

  clUtil::devicePut(x, mNumParticles * sizeof(x[0]), mX);
  clUtil::devicePut(y, mNumParticles * sizeof(x[0]), mY);
  clUtil::devicePut(z, mNumParticles * sizeof(x[0]), mZ);
  clUtil::devicePut(&curBuffer, sizeof(curBuffer), mCurBuffer);

  this->initialize();

  clFinish(clUtil::getCommandQueue());
  
  delete[] x;
  delete[] y;
  delete[] z;
}

WalkerGroupOpenCL::WalkerGroupOpenCL(const char* filename)
{
  FILE* file;
  float* x;
  float* y;
  float* z;
  unsigned int* curBuffer;

  mWalkerStart = 0;

  //Read file
  file = fopen(filename, "r");

  if(file == NULL)
  {
    perror("Error: Could not open grid file.\n");
    exit(1);
  }

  //Read line 1: numWalkers numParticles
  if(fscanf(file, "%u %u", &mNumWalkers, &mNumParticles) != 2)
  {
    printf("Error: Invalid grid file format. Parse error line 1.\n");
    exit(1);
  }

  this->allocate(mNumParticles, mNumWalkers);

  x = new float[mNumWalkers * mNumParticles];
  y = new float[mNumWalkers * mNumParticles];
  z = new float[mNumWalkers * mNumParticles];
  curBuffer = new unsigned int[mNumWalkers];

  //For each walker and particle line: x y z
  for(unsigned int i = 0; i < mNumWalkers * mNumParticles; i++)
  {
    if(fscanf(file, 
              "%E %E %E\n", 
              &x[i], 
              &y[i], 
              &z[i]) != 3)
    {
      printf("Error: Grid file parse error line %d.\n", i + 2);
      exit(1);
    }
  }

  for(size_t i = 0; i < mNumWalkers; i++)
  {
    curBuffer[i] = 1;
  }

  fclose(file);

  //Copy data to device
  clUtil::devicePut(x, mNumParticles * mNumWalkers * sizeof(x[0]), mX);
  clUtil::devicePut(x, mNumParticles * mNumWalkers * sizeof(x[0]), mY);
  clUtil::devicePut(x, mNumParticles * mNumWalkers * sizeof(x[0]), mZ);
  clUtil::devicePut(curBuffer, 
                    mNumParticles * mNumWalkers * sizeof(x[0]), 
                    mCurBuffer);
  
  this->initialize();

  clFinish(clUtil::getCommandQueue());

  delete[] x;
  delete[] y;
  delete[] z;
  delete[] curBuffer;
}

void WalkerGroupOpenCL::writeToDisk(const char* filename)
{
  unsigned int* curBuffer;
  float* x;
  float* y;
  float* z;
  FILE* outputFile;
  char const* errString = NULL;
  cl_int err;
    
  curBuffer = new unsigned int[mNumWalkers];
  CHECKERR(curBuffer == NULL, "Could not allocate memory.", CL_WRITE_FILE_ERR0);
  x = new(nothrow) float[2 * mNumWalkers * mNumParticles];
  CHECKERR(x == NULL, "Could not allocate memory.", CL_WRITE_FILE_ERR1);
  y = new(nothrow) float[2 * mNumWalkers * mNumParticles];
  CHECKERR(y == NULL, "Could not allocate memory.", CL_WRITE_FILE_ERR2);
  z = new(nothrow) float[2 * mNumWalkers * mNumParticles];
  CHECKERR(z == NULL, "Could not allocate memory.", CL_WRITE_FILE_ERR3);

  outputFile = fopen(filename, "w");
  CHECKERR(outputFile == NULL, "Could not open file.", CL_WRITE_FILE_ERR4);

  err = clUtil::deviceGet(curBuffer, 
                          sizeof(curBuffer[0]) * mNumWalkers, 
                          mCurBuffer);
  CHECKERR(err != CL_SUCCESS, "Could not transfer data.", CL_WRITE_FILE_ERR5);
  err = clUtil::deviceGet(x, 
                          sizeof(x[0]) * 2 * mNumParticles * mNumWalkers, 
                          mX);
  CHECKERR(err != CL_SUCCESS, "Could not transfer data.", CL_WRITE_FILE_ERR5);
  err = clUtil::deviceGet(y, 
                          sizeof(y[0]) * 2 * mNumParticles * mNumWalkers, 
                          mY);
  CHECKERR(err != CL_SUCCESS, "Could not transfer data.", CL_WRITE_FILE_ERR5);
  err = clUtil::deviceGet(z, 
                          sizeof(z[0]) * 2 * mNumParticles * mNumWalkers, 
                          mZ);
  CHECKERR(err != CL_SUCCESS, "Could not transfer data.", CL_WRITE_FILE_ERR5);

  fprintf(outputFile, "%d %d\n", mNumWalkers, mNumParticles);

  for(size_t curWalker = 0; curWalker < mNumWalkers; curWalker++)
  {
    for(size_t curParticle = 0; curParticle < mNumParticles; curParticle++)
    {
      unsigned int bufferOffset = curBuffer[curWalker] *
        mNumParticles * 
        mNumWalkers;
      
      fprintf(outputFile,
              "%E %E %E\n",
              x[bufferOffset + curWalker * mNumParticles + curParticle],
              y[bufferOffset + curWalker * mNumParticles + curParticle],
              z[bufferOffset + curWalker * mNumParticles + curParticle]);
    }
  }

  fclose(outputFile);

  delete[] curBuffer;
  delete[] x;
  delete[] y;
  delete[] z;

  return;

CL_WRITE_FILE_ERR5:
  fclose(outputFile);
CL_WRITE_FILE_ERR4:
  delete[] z;
CL_WRITE_FILE_ERR3:
  delete[] y;
CL_WRITE_FILE_ERR2:
  delete[] x;
CL_WRITE_FILE_ERR1:
  delete[] curBuffer;
CL_WRITE_FILE_ERR0:
  printf("%s\n", errorString);
  raise(SIGTRAP);
}

void WalkerGroupOpenCL::debug()
{
  //Debugging
  float* x = new float[2 * mNumWalkers * mNumParticles];
  float* y = new float[2 * mNumWalkers * mNumParticles];
  float* z = new float[2 * mNumWalkers * mNumParticles];
  double* kinetic = new double[2 * mNumWalkers];
  double* potential = new double[2 * mNumWalkers];
  double* waveFunction = new double[2 * mNumWalkers];
  unsigned int* curBuffer = new unsigned int[mNumWalkers];
  double* waveFunctionPartials = new double[mNumWalkers * mNumParticles];
  double* qForceX = new double[mNumWalkers * mNumParticles];
  double* qForceY = new double[mNumWalkers * mNumParticles];
  double* qForceZ = new double[mNumWalkers * mNumParticles];
  double totalEnergy;

  clUtil::deviceGet(x, 2 * sizeof(float) * mNumWalkers * mNumParticles, mX);
  clUtil::deviceGet(y, 2 * sizeof(float) * mNumWalkers * mNumParticles, mY);
  clUtil::deviceGet(z, 2 * sizeof(float) * mNumWalkers * mNumParticles, mZ);
  clUtil::deviceGet(waveFunction, 2 * sizeof(double) * mNumWalkers, mWaveFunction);
  clUtil::deviceGet(kinetic, 2 * sizeof(double) * mNumWalkers, mKinetic);
  clUtil::deviceGet(potential, 2 * sizeof(double) * mNumWalkers, mPotential);
  clUtil::deviceGet(curBuffer, sizeof(unsigned int) * mNumWalkers, mCurBuffer);
  clUtil::deviceGet(waveFunctionPartials,
                    sizeof(double) * mNumParticles * mNumWalkers,
                    mPartialWaveFunction);
  clUtil::deviceGet(qForceX, 
                    sizeof(double) * mNumParticles * mNumWalkers,
                    mQForceX);
  clUtil::deviceGet(qForceY, 
                    sizeof(double) * mNumParticles * mNumWalkers,
                    mQForceY);
  clUtil::deviceGet(qForceZ, 
                    sizeof(double) * mNumParticles * mNumWalkers,
                    mQForceZ);
  clUtil::deviceGet(&totalEnergy, sizeof(double), mTotalEnergy);


  clFinish(clUtil::getCommandQueue());

  delete[] waveFunction;
  delete[] potential;
  delete[] kinetic;
  delete[] curBuffer;
  delete[] x;
  delete[] y;
  delete[] z;
  delete[] waveFunctionPartials;
  delete[] qForceX;
  delete[] qForceY;
  delete[] qForceZ;
}

void WalkerGroupOpenCL::initSprng()
{
#ifdef GPU_SPRNG
  size_t global;
  size_t local;
  cl_mem errDevice;
  cl_int err;

  local = 64;
  global = mNumWalkers % local == 0 ? 
    mNumWalkers : 
    (mNumWalkers / local + 1) * local;

  err = clUtil::alloc(sizeof(err), &errDevice);
  clUtilCheckErrorVoid(err);

  err = 0;
  clUtil::devicePut(&err, sizeof(err), errDevice);

  clUtil_enqueueKernel("init_sprng",
                       1,
                       &global,
                       &local,
                       mGenerators,
                       mWalkerStart,
                       mNumWalkers,
                       errDevice);
  
  clUtil::deviceGet(&err, sizeof(err), errDevice);
  
  if(err != 0)
  {
    printf("GPU SPRNG initialization error.\n");
    raise(SIGTRAP);
  }

  clUtil::free(errDevice);
#else
  //Initialize the generators
  for(size_t curGen = 0; curGen < mNumWalkers; curGen++)
  {
    mGenerators[curGen] = init_sprng(SPRNG_LFG,
                                     mWalkerStart + curGen,
                                     gNumWalkers,
                                     gRandomSeed,
                                     SPRNG_DEFAULT);
  }
#endif
}

void WalkerGroupOpenCL::initialize()
{
  this->initSprng();
  this->computePartials();
  this->reducePartials();

  unsigned int* curBuffer = new unsigned int[mNumWalkers];
  double* totalEnergy = new double[mNumWalkers];

  //Set curent buffer to 0
  for(size_t curWalker = 0; curWalker < mNumWalkers; curWalker++)
  {
    curBuffer[curWalker] = 0;
    totalEnergy[curWalker] = 0.0;
  }

  clUtil::devicePut(curBuffer, sizeof(curBuffer[0]) * mNumWalkers, mCurBuffer);
  clUtil::devicePut(totalEnergy, 
                    sizeof(totalEnergy[0]) * mNumWalkers, 
                    mTotalEnergy);

  delete[] curBuffer;
  delete[] totalEnergy;
}

void WalkerGroupOpenCL::reducePartials(bool initial)
{
  unsigned int* curBufferCPU;
  cl_int err;
  size_t localSize = 64;
  size_t globalSize = mNumWalkers % localSize == 0 ?
    mNumWalkers :
    (mNumWalkers / localSize + 1) * localSize;

  clUtil_enqueueKernel("reducePartials",
                       1,
                       &globalSize,
                       &localSize,
                       mPartialWaveFunction,
                       mPartialPotential,
                       mPartialKinetic,
                       mQForceX,
                       mQForceY,
                       mQForceZ,
                       mCurBuffer,
                       mNumWalkers,
                       mNumParticles,
                       mWaveFunction,
                       mPotential,
                       mKinetic);
}

void WalkerGroupOpenCL::perturb()
{
  size_t global;
  size_t local;

  local = 64;
  global = mNumWalkers % local == 0 ?
    mNumWalkers :
    (mNumWalkers / local + 1) * local;

#ifdef GPU_SPRNG
  clUtil_enqueueKernel("perturb",
                       1,
                       &global,
                       &local,
                       mX,
                       mY,
                       mZ,
                       mGenerators,
                       mCurBuffer,
                       mNumWalkers,
                       mNumParticles);
#else
  //Generate random numbers
  for(size_t curWalker = 0; curWalker < mNumWalkers; curWalker++)
  {
    for(size_t curParticle = 0; curParticle < mNumParticles; curParticle++)
    {
      for(size_t xyz = 0; xyz < 3; xyz++)
      {
        mRandomBuffer[curParticle * 3 * mNumWalkers + 
          xyz * mNumWalkers + 
          curWalker] = (float)sprng(mGenerators[curWalker]);
      }
    }
  }

  //Put them on the GPU
  clUtil::devicePut(mRandomBuffer, 
                    3 * mNumWalkers * mNumParticles * sizeof(mRandomBuffer[0]),
                    mRandomBufferGPU);

  //Call the kernel
  clUtil_enqueueKernel("perturb",
                       1,
                       &global,
                       &local,
                       mX,
                       mY,
                       mZ,
                       mRandomBufferGPU,
                       mCurBuffer,
                       mNumWalkers,
                       mNumParticles);
#endif
}

void WalkerGroupOpenCL::accept()
{
  size_t global;
  size_t local;

  local = 64;
  global = mNumWalkers % local == 0 ? 
    mNumWalkers :
    (mNumWalkers / local + 1) * local;

#ifdef GPU_SPRNG
  clUtil_enqueueKernel("accept",
                       1,
                       &global,
                       &local,
                       mCurBuffer,
                       mNumWalkers,
                       mGenerators,
                       mWaveFunction,
                       mPotential,
                       mKinetic,
                       mTotalEnergy,
                       1);
#else
  //Fill the random buffer with numbers
  for(size_t curWalker = 0; curWalker < mNumWalkers; curWalker++)
  {
    mRandomBuffer[curWalker] = (float)sprng(mGenerators[curWalker]);
  }

  //Transfer it to GPU
  clUtil::devicePut(mRandomBuffer,
                    mNumWalkers * sizeof(mRandomBuffer[0]),
                    mRandomBufferGPU);
  
  clUtil_enqueueKernel("accept",
                       1,
                       &global,
                       &local,
                       mCurBuffer,
                       mNumWalkers,
                       mRandomBufferGPU,
                       mWaveFunction,
                       mPotential,
                       mKinetic,
                       mTotalEnergy,
                       1);
#endif
}

void WalkerGroupOpenCL::compute(cl_event* event)
{
  this->perturb();
  //debug();
  this->computePartials();
  this->reducePartials();
  //debug();
  this->accept();

  //debug();

  mNumIterations++;
}

double WalkerGroupOpenCL::getEnergy()
{
  double* totalEnergyCPU = new double[mNumWalkers];
  double averageEnergy = 0.0;

  clUtil::deviceGet(totalEnergyCPU, mNumWalkers * sizeof(double), mTotalEnergy);

  for(size_t i = 0; i < mNumWalkers; i++)
  {
    averageEnergy += totalEnergyCPU[i];
  }

  delete[] totalEnergyCPU;

  return averageEnergy / mNumIterations;
}

void WalkerGroupOpenCL::computePartials()
{
  size_t localSize[2];
  size_t globalSize[2];
  cl_int err;

  localSize[0] = 64;
  localSize[1] = 1;
  globalSize[0] = mNumParticles % localSize[0] == 0 ? 
    mNumParticles:
    (mNumParticles / localSize[0] + 1) * localSize[0];
  globalSize[1] = mNumWalkers;

  clUtil_enqueueKernel("computeEnergyPartials",
                       2,
                       globalSize,
                       localSize,
                       mX,
                       mY,
                       mZ,
                       mNumParticles,
                       mCurBuffer,
                       mPartialWaveFunction,
                       mPartialPotential,
                       mPartialKinetic,
                       mQForceX,
                       mQForceY,
                       mQForceZ);
}

void WalkerGroupOpenCL::getX(float* output)
{
}

void WalkerGroupOpenCL::getY(float* output)
{
}

void WalkerGroupOpenCL::getZ(float* output)
{
}

WalkerGroupOpenCL::~WalkerGroupOpenCL()
{
  clUtil::free(mCurBuffer);
  clUtil::free(mX);
  clUtil::free(mY);
  clUtil::free(mZ);
  clUtil::free(mWaveFunction);
  clUtil::free(mPartialWaveFunction);
  clUtil::free(mPotential);
  clUtil::free(mPartialPotential);
  clUtil::free(mKinetic);
  clUtil::free(mPartialKinetic);
  clUtil::free(mQForceX);
  clUtil::free(mQForceY);
  clUtil::free(mQForceZ);
  clUtil::free(mTotalEnergy);
#ifdef GPU_SPRNG
  clUtil::free(mGenerators);
#else
  for(size_t i = 0; i < mNumWalkers; i++)
  {
    free_sprng(mGenerators[i]);
  }
  delete[] mGenerators;
  delete[] mRandomBuffer;
  clUtil::free(mRandomBufferGPU);
#endif

}
